/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package it.unitn.disi.wikireputation.util;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;


/**
 *
 * @author carlos
 */
public class RepCalculator {

    //Default parameters for the Reputation Calculator (taken from the paper)
    private double cslack    = 2.20;
    private double cscale    = 13.08;
    private double ctext     = 0.60;
    private double clen      = 0.60;
    private double cpunish   = 19.09;
    private double cmaxrep   = 22026;

    private HashMap<Integer, Author> authorsRep = new HashMap<Integer, Author>();
    public static final double INITIALREP = 0.1;
    


    public RepCalculator(){
        //Default contructor
    }

   /**
   * Creates an instance of RepCalculator
   *
   * @param cslack Parameter to spare authors from punishment when a revision is
   *     is only slightly counterproductive.
   * @param cscale Parameter that specifies how much the reputation should vary
   *     in response to an individual feedback.
   * @param ctext Parameter that specifies how much the feedback should depend
   *     on residual text.
   * @param clen Parameter that specifies how to take into acoount the length of
   *     the original contribution.
   * @param cpunish Parameter that raises the reputation cost of edits that are
   *     later undone.
   *
   */
    public RepCalculator(double cslack, double cscale, double ctext, double clen, double cpunish, double cmaxrep){
        this.cslack    = cslack;
        this.cscale    = cscale;
        this.ctext     = ctext;
        this.clen      = clen;
        this.cpunish   = cpunish;
        this.cmaxrep   = cmaxrep;
    }

    /**
     * Obtains the "Edit Survival Reputation" in three revisions of a text
     * @param textV1 First revision of the text (corresponds to r_{i-1} in the
     *     paper).
     * @param textV2 Second revision of the text (corresponds to r_{i}) in the
     *     paper).
     * @param textV3 Third revision of the text (corresponds to r_{j} in the
     *     paper).
     * @param repAuthor Reputation of author a_{j} at revision r_{j}.
     *
     * @return Reputation increment "q" for the author of revision r_{i}.
     */
    public double getEditSurvivalReputation(String textV1, String textV2, String textV3, double repAuthor ){
        DiffMatchPatch dmp = new DiffMatchPatch();

        double distV1_V2 = (double)dmp.diff_levenshtein(dmp.diff_main(textV1, textV2));

        if(distV1_V2 == 0.0){
            //Don't do anything. Reputation does not change.
            return 0.0;
        }

        double distV1_V3 = (double)dmp.diff_levenshtein(dmp.diff_main(textV1, textV3));
        double distV2_V3 = (double)dmp.diff_levenshtein(dmp.diff_main(textV2, textV3));
        

        double q = (cslack * distV1_V3 - distV2_V3) / distV1_V2;
        
        if (q < 0.0)
            q = q * cpunish;

        q = q * cscale * (1 - ctext) * java.lang.Math.pow(distV1_V2, clen) * java.lang.Math.log(1 + repAuthor);        
        
        return q;
    }

    public void getEditSurvival(String textV1, String textV2, String textV3, Edit edit){
        DiffMatchPatch dmp = new DiffMatchPatch();

        double distV1_V2 = (double)dmp.diff_levenshtein(dmp.diff_main(textV1, textV2));
        double distV1_V3 = (double)dmp.diff_levenshtein(dmp.diff_main(textV1, textV3));
        double distV2_V3 = (double)dmp.diff_levenshtein(dmp.diff_main(textV2, textV3));

        double q = (distV1_V3 - distV2_V3) / distV1_V2;

        edit.setEditLongevity(q);
        edit.setContribSize((int)distV1_V2);

        //System.out.println("("+ distV1_V3 + " - " + distV2_V3 + ") / " + distV1_V2 + " = " + q);
        //System.out.println(q);
    }

    /**
     * Computes the reputation increments for all authors of all revisions of
     * the given "pageId". The updates are stored in an internal data structure
     * that stores the reputation of authors (the data structure can be accessed
     * through the method "getAuthorsRep()".
     * 
     * @param pageId The page to be considered for the computation
     * @throws ClassNotFoundException
     * @throws SQLException
     */
    public void authorReputationIncrement(int pageId) throws ClassNotFoundException, SQLException{
        double q = 0.0;

        
        Statement stmt = DBConnection.getConnection().createStatement();

        String strSql = 
                " select rev_id, rev_text_id, rev_user, rev_user_text from revision where rev_page = ? " +
                " order by rev_id desc " ;

        PreparedStatement psRev = DBConnection.getConnection().prepareStatement(strSql);
        psRev.setInt(1, pageId);

        ResultSet rsRev = psRev.executeQuery();

        rsRev.last();
        int rowCount = rsRev.getRow();

        

        strSql = "select old_text from text where old_id = ?";
        PreparedStatement psText = DBConnection.getConnection().prepareStatement(strSql);               

        for(int row_j = 3; row_j <= rowCount; row_j++){
            rsRev.absolute(row_j);

            int authorId_j = rsRev.getInt("rev_user");

            //We retrieve here the revision "j"
            psText.setInt(1, rsRev.getInt("rev_text_id"));
            ResultSet rsText_j = psText.executeQuery();

            rsText_j.first();

            java.sql.Blob blob_j = rsText_j.getBlob("old_text");
            byte[] bdata_j = blob_j.getBytes(1, (int) blob_j.length());
            String txtRev_j = new String(bdata_j);

            for(int row_i = row_j - 1; (row_i > 1) && (row_j - row_i <= 3); row_i--){
                rsRev.absolute(row_i);
                
                int authorId_i = rsRev.getInt("rev_user");
                String author_i_name = rsRev.getString("rev_user_text");

                if(authorId_i == authorId_j){
                    //Don't do anything. Reputation does not change.
                    continue;
                }

                //We retrieve here the revision "i"
                psText.setInt(1, rsRev.getInt("rev_text_id"));
                ResultSet rsText_i = psText.executeQuery();

                rsText_i.first();
                java.sql.Blob blob_i = rsText_i.getBlob("old_text");
                byte[] bdata_i = blob_i.getBytes(1, (int) blob_i.length());
                String txtRev_i = new String(bdata_i);

                int row_i_1 = row_i - 1;

                rsRev.absolute(row_i_1);

                //We retrieve here the revision "i-1"
                psText.setInt(1, rsRev.getInt("rev_text_id"));
                ResultSet rsText_i_1 = psText.executeQuery();

                rsText_i_1.first();
                java.sql.Blob blob_i_1 = rsText_i_1.getBlob("old_text");
                byte[] bdata_i_1 = blob_i_1.getBytes(1, (int) blob_i_1.length());
                String txtRev_i_1 = new String(bdata_i_1);

                Author author_i = getAuthorsRep().get(authorId_i);
                if(author_i == null){
                    author_i = new Author(authorId_i, author_i_name, INITIALREP);
                }

                Author author_j = getAuthorsRep().get(authorId_j);
                if(author_j == null){
                    author_j = new Author(authorId_j, "", INITIALREP);
                }
                    
                
                q = this.getEditSurvivalReputation(txtRev_i_1, txtRev_i, txtRev_j, author_j.getReputation());

                author_i.setReputation(author_i.getReputation() + q);

                //The paper says that it is ensured that reputation never becomes
                //negative, but it does not say how. Therefore, we apply the
                //following logic:
                if(author_i.getReputation() < 0.0)
                    author_i.setReputation(0.1);

                if(author_i.getReputation() > cmaxrep)
                    author_i.setReputation(cmaxrep);

                
                getAuthorsRep().put(authorId_i, author_i);

            }            

        }


        rsRev.beforeFirst();
        while(rsRev.next() && rsRev.getRow() >= 3){
            psText.setInt(1, rsRev.getInt("rev_text_id"));
            ResultSet rsText = psText.executeQuery();

            java.sql.Blob blob = rsText.getBlob("t");
            byte[] bdata = blob.getBytes(1, (int) blob.length());
            String txtRev3 = new String(bdata);

            
            
            
        }

    }

    /**
     * Obtains a HashMap containing the list of authors with the computed
     * reputation
     * @return A HashMap containing the list of authors and their reputation
     */
    public HashMap<Integer, Author> getAuthorsRep() {
        return authorsRep;
    }
    
}
